package com.sen.rbac.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sen.common.common.bo.SysAccountBO;
import com.sen.common.common.constrant.CommonConstrant;
import com.sen.common.common.enums.CommonEnum;
import com.sen.common.common.response.PageVo;
import com.sen.common.user.config.UserContextHolder;
import com.sen.rbac.constant.RbacConstant;
import com.sen.rbac.dto.SysAccountDto;
import com.sen.rbac.exception.RbacException;
import com.sen.rbac.mapper.SysAccountDao;
import com.sen.rbac.mapper.SysRoleAccountDao;
import com.sen.rbac.mapper.SysRoleDao;
import com.sen.rbac.mapper.SysTenantAccountDao;
import com.sen.rbac.model.SysAccount;
import com.sen.rbac.model.SysRole;
import com.sen.rbac.model.SysRoleAccount;
import com.sen.rbac.model.SysTenantAccount;
import com.sen.rbac.service.AccountService;
import com.sen.rbac.vo.SysAccountVO;
import com.sen.rbac.vo.SysRoleVO;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.nio.charset.Charset;
import java.util.List;
import java.util.stream.Collectors;

import static com.sen.rbac.enums.RbacMsgEnum.*;


/**
 * @author esjiang
 */
@Service
public class AccountServiceImpl implements AccountService, InitializingBean {

    @Resource
    SysAccountDao sysAccountDao ;

    @Resource
    ModelMapper modelMapper ;

    @Resource
    SysRoleAccountDao sysRoleAccountDao;

    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder() ;

    @Value("${publicKey}")
    private String publicKey ;

    @Value("${privateKey}")
    private String privateKey ;

    private RSA rsa ;

    @Resource
    SysTenantAccountDao sysTenantAccountDao ;

    @Resource
    SysRoleDao roleDao;


    /**
     * 判断是否允许登录
     * 1，登录运营平台只允许admin和运营专员
     * @param systemCode
     * @param userType
     * @return
     */
    private boolean isAllowLogin(String systemCode,String userType){
        if (StrUtil.equals(systemCode, RbacConstant.platform_operate)){
            if (StrUtil.equals(userType,RbacConstant.user_type_1) || StrUtil.equals(userType,RbacConstant.user_type_2)){
                return true ;
            }else {
                return false ;
            }
        }
        return true ;
    }

    /**
     * 根据用户名查询用户信息
     * @param userName
     * @return
     */
    @Override
    public SysAccountBO selectAccountByUserName(String userName, String tenantCode, String systemCode) throws RbacException {
        SysAccount account = getAccount(userName);
        if (ObjectUtil.isNull(account)){
            throw new RbacException(ACCOUNT_NOT_EXIST) ;
        }
        if (StrUtil.equals(account.getEnable(), CommonEnum.ENABLE_NO.getCode())){
            throw new RbacException(account_disabled) ;
        }
        if (StrUtil.equals(account.getLocked(),CommonEnum.LOCK_YES.getCode())){
            throw new RbacException(account_locked) ;
        }
        if (!isAllowLogin(systemCode,account.getUserType())){
            throw new RbacException(not_allow_login);
        }
        SysAccountBO accountBO = modelMapper.map(account, SysAccountBO.class);
        accountBO.setEntityCode(account.getEntityCode());
        /** 设置区划，只设置运营专员的区划，admin和第三方区划为空，其他账户的区划通过前端获取 */
        if (StrUtil.equals(RbacConstant.user_type_2,account.getUserType())){
            SysTenantAccount sysTenantAccount = sysTenantAccountDao.selectOne(Wrappers.<SysTenantAccount>lambdaQuery().select(SysTenantAccount::getTenantCode).eq(SysTenantAccount::getUserName, userName));
            if (ObjectUtil.isNotNull(sysTenantAccount)){
                accountBO.setTenantCode(sysTenantAccount.getTenantCode());
            }
        } else {
            accountBO.setTenantCode(tenantCode);
        }
        /** 查询角色信息 如果是admin则记录为空 */
        List<SysRole> rolesByUserNameTenantCode = sysRoleAccountDao.findRolesByUserNameTenantCode(userName, tenantCode);
        if (CollUtil.isNotEmpty(rolesByUserNameTenantCode)){
            String roleCodes = rolesByUserNameTenantCode.stream().map(record -> record.getName()).collect(Collectors.joining(","));
            accountBO.setRoleCodes(roleCodes);
        }
        return accountBO;
    }

    /**
     * 新增账户
     * 区划信息由前台注入
     * @param accountDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean addAccount(SysAccountDto accountDto){
        if (!checkUniqueAccount(accountDto.getUserName())){
            throw new RbacException(ACCOUNT_DUPLICATE_ERROR);
        }
        SysAccount record = modelMapper.map(accountDto, SysAccount.class);
        String encodePassword = bCryptPasswordEncoder.encode(rsa.decryptStr(accountDto.getPassword(),KeyType.PrivateKey));
        record.setPassword(encodePassword);
        record.setCreatedTime(DateUtil.date());
        /** 写入账户表 */
        return sysAccountDao.insert(record) == 1;
    }

    /**
     * 更新账户
     * @param accountDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean updateAccount(SysAccountDto accountDto){
        String password = accountDto.getPassword();
        if (StrUtil.isNotEmpty(password)){
            accountDto.setPassword(bCryptPasswordEncoder.encode(rsa.decryptStr(password,KeyType.PrivateKey)));
        }
        return sysAccountDao.update(modelMapper.map(accountDto,SysAccount.class),
                Wrappers.<SysAccount>lambdaQuery().eq(SysAccount::getUserName,accountDto.getUserName())) == 1 ;
    }

    /**
     * 删除账户
     * @param userName
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean deleteAccount(String userName){
        SysAccount sysAccount = getAccount(userName);
        sysAccount.setDelFlag(CommonEnum.DEL_YES.getCode());
        sysAccount.setEnable(CommonEnum.ENABLE_NO.getCode());
        return sysAccountDao.update(sysAccount,Wrappers.<SysAccount>lambdaQuery().eq(SysAccount::getUserName,userName)) == 1;
    }

    /**
     * 查询账户信息
     * @param accountDto
     * @return
     */
    @Override
    public PageVo<SysAccountVO> list(SysAccountDto accountDto){
        LambdaQueryWrapper<SysAccount> objectLambdaQueryWrapper = Wrappers.lambdaQuery();
        if (StrUtil.isNotEmpty(accountDto.getUserName())){
            objectLambdaQueryWrapper.like(SysAccount::getUserName,accountDto.getUserName());
        }
        if (StrUtil.isNotBlank(accountDto.getEnable())){
            objectLambdaQueryWrapper.eq(SysAccount::getEnable,accountDto.getEnable());
        }
        if (StrUtil.isNotBlank(accountDto.getDelFlag())){
            objectLambdaQueryWrapper.eq(SysAccount::getDelFlag,accountDto.getDelFlag());
        }
        if (!UserContextHolder.isSuperAdmin()){
            objectLambdaQueryWrapper.eq(SysAccount::getEntityCode,UserContextHolder.getPeople().getEntityCode()) ;
        }
        objectLambdaQueryWrapper.notIn(SysAccount::getUserName,UserContextHolder.getUser(), CommonConstrant.SUPER_ADMIN) ;
        Page<SysAccount> sysAccountPage = sysAccountDao.selectPage(new Page<>(accountDto.getPage(), accountDto.getPageSize()), objectLambdaQueryWrapper);
        PageVo<SysAccountVO> sysAccountVOPageVo = new PageVo<>();
        if (CollectionUtil.isNotEmpty(sysAccountPage.getRecords())){
            sysAccountVOPageVo.setTotal(sysAccountPage.getTotal());
            List<SysAccountVO> collect = sysAccountPage.getRecords().stream().map(sysAccount -> modelMapper.map(sysAccount, SysAccountVO.class)).collect(Collectors.toList());
            sysAccountVOPageVo.setList(collect) ;
        }
        return sysAccountVOPageVo ;
    }

    /**
     * 重置密码
     * @param userName
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean resetPassword(String userName) throws RbacException {
        SysAccount account = getAccount(userName);
        account.setPassword(bCryptPasswordEncoder.encode(RbacConstant.DEFAULT_PASSWORD));
        return sysAccountDao.update(account,Wrappers.<SysAccount>lambdaQuery().eq(SysAccount::getUserName,userName)) == 1 ;
    }

    /**
     * 检查账户名是否唯一
     * @param userName
     * @return
     */
    @Override
    public boolean checkUniqueAccount(String userName){
        List<SysAccount> sysAccounts = sysAccountDao.selectList(Wrappers.<SysAccount>lambdaQuery().eq(SysAccount::getUserName, userName));
        return CollUtil.isEmpty(sysAccounts) ;
    }

    /**
     * 账户启用停用
     * @param userName
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean enableAccount(String userName, String enable) throws RbacException {
        SysAccount account = getAccount(userName) ;
        account.setEnable(enable);
        return sysAccountDao.update(account,Wrappers.<SysAccount>lambdaQuery().eq(SysAccount::getUserName,userName)) == 1;
    }

    /**
     * 关联角色
     * @param userName
     * @param roleCodes
     * @return
     */
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean relatedRole(String userName, List<String> roleCodes) {
        /** 先删除 */
        sysRoleAccountDao.delete(Wrappers.<SysRoleAccount>lambdaQuery().eq(SysRoleAccount::getUserName,userName));
        /** 后添加 */
        roleCodes.forEach(roleCode -> sysRoleAccountDao.insert(SysRoleAccount.builder().userName(userName).roleCode(roleCode).build()));
        return true;
    }

    /**
     * 查询账户下的角色信息
     * @param userName
     * @return
     */
    @Override
    public List<SysRoleVO> getRolesByUserName(String userName) {
        List<SysRole> rolesByUserName = sysRoleAccountDao.findRolesByUserName(userName);
        return rolesByUserName.stream().map(sysRole -> modelMapper.map(sysRole, SysRoleVO.class)).collect(Collectors.toList());
    }

    /**
     * 修改密码
     * @param sysAccountDto
     * @return
     */
    @Override
    public boolean updatePassword(SysAccountDto sysAccountDto){
        String password =rsa.decryptStr(sysAccountDto.getPassword(),KeyType.PrivateKey, Charset.forName("utf-8"));
        String newPassword =rsa.decryptStr( sysAccountDto.getNewPassword(),KeyType.PrivateKey, Charset.forName("utf-8"));

        /** 校验用户是否存在 */
        SysAccount account = sysAccountDao.selectOne(Wrappers.<SysAccount>lambdaQuery().eq(SysAccount::getUserName, sysAccountDto.getUserName())
                .eq(SysAccount::getEnable,CommonEnum.ENABLE_YES.getCode()).eq(SysAccount::getDelFlag,CommonEnum.DEL_NO.getCode()));
        if (ObjectUtil.isNull(account)){
            throw new RbacException(ACCOUNT_NOT_EXIST);
        }
        /** 校验原密码是否正确 */
        boolean matches = bCryptPasswordEncoder.matches(password, account.getPassword());
        if (!matches){
            throw new RbacException(ACCOUNT_PASSWORD_ERROR);
        }
        /** 设置新密码 */
        account.setPassword(bCryptPasswordEncoder.encode(newPassword));
        return sysAccountDao.updateById(account) == 1;
    }

    private SysAccount getAccount(String userName) throws RbacException {
        List<SysAccount> sysAccounts = sysAccountDao.selectList(Wrappers.<SysAccount>lambdaQuery()
                .eq(SysAccount::getUserName,userName)
        );
        if (CollectionUtil.isEmpty(sysAccounts)){
            throw new RbacException(ACCOUNT_NOT_EXIST) ;
        }
        if (sysAccounts.size() != 1){
            throw new RbacException(ACCOUNT_DATA_EXCEPTION);
        }
        return sysAccounts.get(0);
    }

    @Override
    public void afterPropertiesSet(){
        rsa = SecureUtil.rsa(privateKey,publicKey) ;
    }


    /**
     * 根据用户名查询用户信息
     * @param userName
     * @return
     */
    @Override
    public SysAccountBO selectAccountByUserName(String userName) throws RbacException {
        SysAccount account = getAccount(userName);
        SysAccountBO accountBO= new SysAccountBO();
        if(accountBO!=null) {
          accountBO = modelMapper.map(account, SysAccountBO.class);
        }
        return accountBO;
    }

}
